"use strict";

/**将指定的文件或目录压缩成不加密的Zip文件
 * @param needCompressTarget 需要压缩的文件或目录
 * @param outputZipFileFullPath 输出Zip压缩文件的全路径，默认为需要压缩的数据所在的位置。
 * @description 本函数依赖linux系统的zip和unzip程序，以及windows的cscript程序，使用本函数之前，zip、unzip或cscript程序是否能正常使用。
 * @description 本函数暂支持linux、windows操作系统，暂未支持macOS系统。
 * @description 本函数从调用开始到结束，没有返回值，表示压缩成功。抛出异常表示压缩失败。
 * @description Windows系统下调用该函数压缩文件时，弹出的压缩进度对话框是正常的，请不要关闭或点击对话框上的取消按钮，否则压缩失败。
 * @description 遇到安全防护软件拦截时，请允许全部操作，以免压缩失败。
 * @description 本函数暂不支持加密压缩文件。
 * */
function zip_EnCompress(needCompressTarget, outputZipFileFullPath) {
    var child_process_module = require("child_process");
    var path_module = require("path");
    var fs_module = require("fs");
    var node_process = require("process");
    var compressTargetLocation;
    var compressMakeFileName;

    function deepCopy(source) {
        var cp1;
        var cp2;
        try {
            cp1 = JSON.stringify(source);
            cp2 = JSON.parse(cp1);
            return cp2;
        } catch (e) {
            return source;
        }
    }

    function EraseMatchArrayItems(sourceArray, needEraseItem, notModifySourceArray) {
        if (typeof notModifySourceArray !== "boolean") {
            notModifySourceArray = false;
        }
        if (Object.prototype.toString.call(sourceArray) !== "[object Array]") {
            throw new Error("sourceArray Argument must be an array");
        }
        if (needEraseItem !== undefined) {
            var final = [];
            var found = false;
            while (true) {
                found = false;
                var funcResult = undefined;
                sourceArray.forEach(function (value, index, array) {
                    switch (typeof needEraseItem) {
                        case "function":
                            if (Object.prototype.toString.call(needEraseItem) === "[object Function]") {
                                funcResult = needEraseItem(value);
                                if (funcResult !== undefined) {
                                    if (typeof funcResult === "boolean") {
                                        if (funcResult) {
                                            found = true;
                                            if (!notModifySourceArray) {
                                                array.splice(index, 1);
                                            }
                                        } else {
                                            if (notModifySourceArray) {
                                                final.push(deepCopy(value));
                                            }
                                        }
                                    }
                                }
                            } else {
                                throw new Error("is Only Support Not Async Functions!");
                            }
                            break;
                        case "string":
                            if (String(needEraseItem).toString() === String(value).toString()) {
                                found = true;
                                if (!notModifySourceArray) {
                                    array.splice(index, 1);
                                }
                            } else {
                                if (notModifySourceArray) {
                                    final.push(deepCopy(value));
                                }
                            }
                            break;
                        case "number":
                            if (Number(needEraseItem).valueOf() === Number(value).valueOf()) {
                                found = true;
                                if (!notModifySourceArray) {
                                    array.splice(index, 1);
                                }
                            } else {
                                if (notModifySourceArray) {
                                    final.push(deepCopy(value));
                                }
                            }
                            break;
                        default:
                            throw new Error("needEraseItem is Only Support String Or Number Or Functions");
                    }
                });
                if (!found) {
                    if (notModifySourceArray) {
                        return final;
                    } else {
                        return undefined;
                    }
                }
            }
        } else {
            throw new Error("not Exist need Match Item");
        }
    }

    function confirmName(dir, fName) {
        var detectResult;

        function recs(n_name) {
            var se = path_module.join(dir, `${n_name}.zip`);
            if (fs_module.existsSync(se)) {
                return false;
            } else {
                return true;
            }
        }

        var tx1 = fName;
        var tx1_count = 1;
        while (true) {
            var sc = recs(tx1);
            if (sc) {
                detectResult = path_module.join(dir, `${tx1}.zip`);
                break;
            } else {
                tx1 = `${fName}-${tx1_count}`;
                tx1_count++;
            }
        }
        return detectResult;
    }

    function random_s(length) {
        var final_string = "";
        if (typeof length === "number" && isFinite(length) && !isNaN(length)) {
            if (length <= 0) {
                length = 7;
            }
        } else {
            length = 7
        }

        function random_b() {
            return parseFloat(Math.random().toString()).toFixed(1) >= 0.5;
        }

        var sa = "abcdefghijklmnopqrstuvwxyz";
        var SA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        var char_select;
        switch (node_process.platform) {
            case "linux":
                while (true) {
                    if (String(final_string).length >= length) {
                        break;
                    }
                    char_select = parseInt(26 * Math.random() + "");
                    if (random_b()) {
                        final_string = final_string + String(sa).charAt(char_select);
                    } else {
                        final_string = final_string + String(SA).charAt(char_select);
                    }
                }
                break;
            case "win32":
                var win32_sel = random_b();
                if (win32_sel) {
                    while (true) {
                        if (String(final_string).length >= length) {
                            break;
                        }
                        char_select = parseInt(26 * Math.random() + "");
                        final_string = final_string + String(sa).charAt(char_select);
                    }
                } else {
                    while (true) {
                        if (String(final_string).length >= length) {
                            break;
                        }
                        char_select = parseInt(26 * Math.random() + "");
                        final_string = final_string + String(SA).charAt(char_select);
                    }
                }
                break;
        }
        return final_string;
    }

    function dirPer(path) {
        try {
            var svx = path_module.dirname(path);
            var st = path_module.join(svx, random_s(100));
            fs_module.mkdirSync(st);
            fs_module.rmdirSync(st);
            return true;
        } catch (e) {
            return false;
        }
    }

    if (fs_module.existsSync(needCompressTarget)) {
        compressTargetLocation = path_module.dirname(needCompressTarget);
        if (outputZipFileFullPath) {
            if (fs_module.existsSync(outputZipFileFullPath)) {
                throw new Error("The target already exists and cannot be operated.");
            } else {
                var s1 = path_module.dirname(outputZipFileFullPath);
                if (s1 === ".") {
                    if (dirPer(path_module.join(compressTargetLocation, outputZipFileFullPath))) {
                        compressMakeFileName = path_module.join(compressTargetLocation, outputZipFileFullPath);
                    } else {
                        throw new Error("Target Path Invalid.");
                    }
                } else {
                    var s2;
                    switch (node_process.platform) {
                        case "win32":
                            s2 = String(outputZipFileFullPath).split("\\");
                            break;
                        case "linux":
                            s2 = String(outputZipFileFullPath).split("/");
                            break;
                    }
                    EraseMatchArrayItems(s2, "");
                    var acv = false;
                    switch (node_process.platform) {
                        case "win32":
                            acv = fs_module.statSync(s1).mode >= 16822
                            break;
                        case "linux":
                            acv = fs_module.statSync(s1).mode >= 16832
                            break;
                    }
                    if (fs_module.existsSync(s1) && acv) {
                        if (dirPer(outputZipFileFullPath)) {
                            compressMakeFileName = deepCopy(outputZipFileFullPath);
                        } else {
                            throw new Error("Target Path Invalid.");
                        }
                    } else {
                        throw new Error("Target Path Invalid.");
                    }
                }
            }
        } else {
            var preTargetOrigin = fs_module.statSync(needCompressTarget);
            var tmp1, tmp2;
            switch (node_process.platform) {
                case "linux":
                    tmp1 = String(needCompressTarget).split("/");
                    break;
                case "win32":
                    tmp1 = String(needCompressTarget).split("\\");
                    break;
            }
            EraseMatchArrayItems(tmp1, "");
            tmp2 = tmp1.pop();
            tmp1.splice(0);
            if (preTargetOrigin.isDirectory()) {
                compressMakeFileName = confirmName(compressTargetLocation, tmp2);
                if (!dirPer(compressMakeFileName)) {
                    throw new Error("Target Path Invalid.");
                }
            } else if (preTargetOrigin.isFile()) {
                compressMakeFileName = confirmName(compressTargetLocation, tmp2);
                if (!dirPer(compressMakeFileName)) {
                    throw new Error("Target Path Invalid.");
                }
            } else {
                throw new Error("need compress target must is Directory or is Files");
            }
        }
        var targetName;
        var v1;
        switch (node_process.platform) {
            case "linux":
                v1 = String(needCompressTarget).split("/");
                break;
            case "win32":
                v1 = String(needCompressTarget).split("\\");
                break;
        }
        EraseMatchArrayItems(v1, "");
        targetName = deepCopy(v1[v1.length - 1]);
        try {
            switch (node_process.platform) {
                case "win32":
                    var cmd = `cscript ${path_module.join(__dirname, "zip.vbs")} -c ${compressMakeFileName} ${path_module.join(compressTargetLocation, targetName)}`;
                    child_process_module.execSync(cmd, {
                        cwd: compressTargetLocation,
                        encoding: "utf8"
                    });
                    break;
                case "linux":
                    child_process_module.execSync(`zip ${compressMakeFileName} ${targetName}/** -r -X -D`, {
                        cwd: compressTargetLocation,
                        encoding: "utf8"
                    });
                    break;
                default:
                    throw new Error("This operating system is not supported");
            }
        } catch (e) {
            throw e;
        }
    } else {
        throw new Error("Unable to find the target that needs to be compressed");
    }
}

/**将指定的Zip格式的压缩文件解压
 * @param zipFile 需要解压缩的Zip格式的压缩文件
 * @param unCompressTarget 解压输出Zip压缩文件内容的全路径，默认为压缩文件所在的位置。
 * @description 本函数依赖linux系统的zip和unzip程序，以及windows系统的cscript程序，使用本函数之前，zip、unzip或cscript程序是否能正常使用。
 * @description 本函数暂支持linux、windows操作系统，暂未支持macOS系统。
 * @description 本函数从调用开始到结束，没有返回值，表示解压缩成功。抛出异常表示解压缩失败。
 * @description Windows系统下调用该函数解压文件时，弹出的解压进度对话框是正常的，请不要关闭或点击对话框上的取消按钮，否则解压失败。
 * @description 遇到安全防护软件拦截时，请允许全部操作，以免解压失败。
 * @description 本函数暂不支持解压加密的压缩文件。
 * */
function zip_DeCompress(zipFile, unCompressTarget) {
    var child_process_module = require("child_process");
    var path_module = require("path");
    var fs_module = require("fs");
    var node_process = require("process");
    var unCompressTargetLocation;
    var final_Location;

    function random_b() {
        return parseFloat(Math.random().toString()).toFixed(1) >= 0.5;
    }

    function random_s(length) {
        var final_string = "";
        if (typeof length === "number" && isFinite(length) && !isNaN(length)) {
            if (length <= 0) {
                length = 7;
            }
        } else {
            length = 7
        }

        var sa = "abcdefghijklmnopqrstuvwxyz";
        var SA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        var char_select;
        switch (node_process.platform) {
            case "linux":
                while (true) {
                    if (String(final_string).length >= length) {
                        break;
                    }
                    char_select = parseInt(26 * Math.random() + "");
                    if (random_b()) {
                        final_string = final_string + String(sa).charAt(char_select);
                    } else {
                        final_string = final_string + String(SA).charAt(char_select);
                    }
                }
                break;
            case "win32":
                var win32_sel = random_b();
                if (win32_sel) {
                    while (true) {
                        if (String(final_string).length >= length) {
                            break;
                        }
                        char_select = parseInt(26 * Math.random() + "");
                        final_string = final_string + String(sa).charAt(char_select);
                    }
                } else {
                    while (true) {
                        if (String(final_string).length >= length) {
                            break;
                        }
                        char_select = parseInt(26 * Math.random() + "");
                        final_string = final_string + String(SA).charAt(char_select);
                    }
                }
                break;
        }
        return final_string;
    }

    function deepCopy(source) {
        var cp1;
        var cp2;
        try {
            cp1 = JSON.stringify(source);
            cp2 = JSON.parse(cp1);
            return cp2;
        } catch (e) {
            return source;
        }
    }

    function EraseMatchArrayItems(sourceArray, needEraseItem, notModifySourceArray) {
        if (typeof notModifySourceArray !== "boolean") {
            notModifySourceArray = false;
        }
        if (Object.prototype.toString.call(sourceArray) !== "[object Array]") {
            throw new Error("sourceArray Argument must be an array");
        }
        if (needEraseItem !== undefined) {
            var final = [];
            var found = false;
            while (true) {
                found = false;
                var funcResult = undefined;
                sourceArray.forEach(function (value, index, array) {
                    switch (typeof needEraseItem) {
                        case "function":
                            if (Object.prototype.toString.call(needEraseItem) === "[object Function]") {
                                funcResult = needEraseItem(value);
                                if (funcResult !== undefined) {
                                    if (typeof funcResult === "boolean") {
                                        if (funcResult) {
                                            found = true;
                                            if (!notModifySourceArray) {
                                                array.splice(index, 1);
                                            }
                                        } else {
                                            if (notModifySourceArray) {
                                                final.push(deepCopy(value));
                                            }
                                        }
                                    }
                                }
                            } else {
                                throw new Error("is Only Support Not Async Functions!");
                            }
                            break;
                        case "string":
                            if (String(needEraseItem).toString() === String(value).toString()) {
                                found = true;
                                if (!notModifySourceArray) {
                                    array.splice(index, 1);
                                }
                            } else {
                                if (notModifySourceArray) {
                                    final.push(deepCopy(value));
                                }
                            }
                            break;
                        case "number":
                            if (Number(needEraseItem).valueOf() === Number(value).valueOf()) {
                                found = true;
                                if (!notModifySourceArray) {
                                    array.splice(index, 1);
                                }
                            } else {
                                if (notModifySourceArray) {
                                    final.push(deepCopy(value));
                                }
                            }
                            break;
                        default:
                            throw new Error("needEraseItem is Only Support String Or Number Or Functions");
                    }
                });
                if (!found) {
                    if (notModifySourceArray) {
                        return final;
                    } else {
                        return undefined;
                    }
                }
            }
        } else {
            throw new Error("not Exist need Match Item");
        }
    }

    function dirPer(path) {
        try {
            var svx = path_module.dirname(path)
            var st = path_module.join(svx, random_s(100));
            fs_module.mkdirSync(st);
            fs_module.rmdirSync(st);
            return true;
        } catch (e) {
            return false;
        }
    }

    if (fs_module.existsSync(zipFile) && fs_module.statSync(zipFile).isFile()) {
        unCompressTargetLocation = path_module.dirname(zipFile);
        if (String(path_module.extname(zipFile)).toLowerCase() === ".zip") {
            if (unCompressTarget) {
                if (fs_module.existsSync(unCompressTarget) && fs_module.statSync(unCompressTarget).isDirectory()) {
                    if (dirPer(unCompressTarget)) {
                        final_Location = deepCopy(unCompressTarget);
                    } else {
                        throw new Error("Invalid decompression target");
                    }
                } else {
                    var vn_1;
                    var vn_2;
                    switch (node_process.platform) {
                        case "win32":
                            vn_1 = String(unCompressTarget).split("\\");
                            break;
                        case "linux":
                            vn_1 = String(unCompressTarget).split("/");
                            break;
                    }
                    EraseMatchArrayItems(vn_1, "");
                    EraseMatchArrayItems(vn_1, ".");
                    if (vn_1.length > 1) {
                        switch (node_process.platform) {
                            case "linux":
                                vn_2 = path_module.join(unCompressTargetLocation, vn_1.join("/"));
                                break;
                            case "win32":
                                vn_2 = path_module.join(unCompressTargetLocation, vn_1.join("\\"));
                                break;
                        }
                    } else {
                        vn_2 = path_module.join(unCompressTargetLocation, vn_1[0]);
                    }
                    if (fs_module.statSync(vn_2).isDirectory() && dirPer(vn_2)) {
                        final_Location = deepCopy(vn_2);
                    } else {
                        throw new Error("Invalid decompression target");
                    }
                }
            } else {
                if (dirPer(unCompressTargetLocation)) {
                    final_Location = deepCopy(unCompressTargetLocation);
                } else {
                    throw new Error("Invalid decompression target");
                }
            }
            try {
                switch (node_process.platform) {
                    case "linux":
                        child_process_module.execSync(`unzip ${zipFile} -d ${final_Location}`, {
                            cwd: unCompressTargetLocation,
                            encoding: "utf8"
                        });
                        break;
                    case "win32":
                        child_process_module.execSync(`cscript ${path_module.join(__dirname, "zip.vbs")} -d ${zipFile} ${final_Location}`, {
                            cwd: unCompressTargetLocation,
                            encoding: "utf8"
                        })
                        break;
                    default:
                        throw new Error("This operating system is not supported");
                }
            } catch (e) {
                throw e;
            }
        } else {
            throw new Error("Need to decompress a compressed file with a destination that is not in zip format.");
        }
    } else {
        throw new Error("Unable to find the file that needs to be uncompressed.");
    }
}

module.exports = {
    zip_EnCompress,
    zip_DeCompress
}